#pragma once
#include "../3rd/cpp-httplib/httplib.h"
#include "../3rd/rapidjson/include/rapidjson/document.h"
#include "utils/dbutils.hpp"
#include "utils/kvutils.hpp"
#include "utils/transutils.hpp"

using namespace httplib;
using namespace rapidjson;

class User_ {
 public:
  std::string username;
  std::string phoneNumber;
  std::string email;

  template <typename Writer>
  void Serialize(Writer &writer) const {
    writer.StartObject();
    writer.String("username");
    writer.String(username.c_str());
    writer.String("phoneNumber");
    writer.String(phoneNumber.c_str());
    writer.String("email");
    writer.String(email.c_str());
    writer.EndObject();
  }
};

void Register(const Request &req, Response &res) {
  res.set_header("Access-Control-Allow-Origin", "*");
  // std::cout << "register" << std::endl;

  // std::cout << req.body << std::endl;

  Document doc;
  doc.Parse(req.body.c_str());
  std::string username = doc["username"].GetString();
  std::string password = doc["password"].GetString();
  std::string phone_number = doc["phoneNumber"].GetString();
  std::string email = doc["email"].GetString();

  auto kv = KVWrapper::GetInstance("user");
  auto db = DBWrapper::GetInstance();

  if (kv->PutIfMissing(username, password) &&
      db->Insert("users",
                 {Qstring(username), Qstring(phone_number), Qstring(email)})) {
    res.status = 200;
  } else {
    res.status = 202;
  }
}

void Login(const Request &req, Response &res) {
  res.set_header("Access-Control-Allow-Origin", "*");

  Document doc;
  doc.Parse(req.body.c_str());
  auto username = doc["username"].GetString();
  auto password = doc["password"].GetString();

  auto kv = KVWrapper::GetInstance("user");
  auto db = DBWrapper::GetInstance();
  // kv->Iterate(); // for debug
  // db->Iterate("users");  // for debug

  if (kv->Exist(username) && password == kv->Get(username)) {
    std::string sql = "select * from users where ";
    sql += "username = " + Qstring(username) + ";";
    auto result = db->Select(sql);

    hassert(result.get() != nullptr, "result is nullptr");

    User_ user{username, result->begin()->at(1), result->begin()->at(2)};
    res.set_content(Object2Json(user), "application/json");
    res.status = 200;
  } else {
    res.status = 202;
  }
}

void ChangePassword(const Request &req, Response &res) {
  res.set_header("Access-Control-Allow-Origin", "*");

  Document doc;
  doc.Parse(req.body.c_str());
  auto username = doc["username"].GetString();
  auto password = doc["password"].GetString();
  auto newPassword = doc["newPassword"].GetString();

  auto kv = KVWrapper::GetInstance("user");
  hassert(kv->Exist(username), "ChangePassword fatal");

  if (kv->Get(username) != password) {
    res.status = 202;
    return;
  }

  kv->Put(username, newPassword);
  res.status = 200;
}